package com.novartis.nims.util;

import java.io.IOException;
import java.io.InputStream;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class SHA1Hash {
	
	private static String convertToHex(byte[] data) {
		// a faster version?
		// TODO: profile it. also, array index faster than toHexString?
//		StringBuffer sb = new StringBuffer(digest.length * 2);
//	    for (int i = 0; i < digest.length; i++) {
//	    	byte currentByte = (byte)(digest[i] & 0xF0); // Strip off high nibble
//	    	currentByte = (byte)(currentByte >>> 4); // shift the bits down
//	    	currentByte = (byte)(currentByte & 0x0F); // in case high order bit is set
//			sb.append(Integer.toHexString(currentByte)); // convert the nibble to a String Character
//	    	currentByte = (byte)(digest[i] & 0x0F); // Strip off low nibble 
//			sb.append(Integer.toHexString(currentByte)); // convert the nibble to a String Character
//	    }
//	    
//	    return sb.toString();
	    
		StringBuilder digestAsHexString = new StringBuilder();
	    for (int i = 0; i < data.length; i++) {
	    	String hex = Integer.toHexString(data[i]);
	    	if (hex.length() == 1) {
	    		// left pad to 2 digits if necessary
	    		digestAsHexString.append("0");
		    	digestAsHexString.append(hex);
	    	} else {
	    		//left trim ffffff from hex string. required because implicit cast from byte to int
		        // causes negative bytes to be converted to byte value + 2^32
	    		digestAsHexString.append(hex.substring(hex.length() - 2));
	    	}
	    }
	    
        return digestAsHexString.toString();
	}
	
	static public String hash(InputStream input) throws SHA1HashException {
		MessageDigest md;
		try {
			md = MessageDigest.getInstance("SHA1");
		} catch (NoSuchAlgorithmException e) {
			throw new SHA1HashException("Couldn't generate SHA1 hash for input stream.", e);
		}
		DigestInputStream dis = new DigestInputStream(input, md);
		
		// TODO: optimize this by intelligently tweaking buffer size? 
		byte[] buffer = new byte[8192]; // arbitrary buffer size
		try {
			try {
				while (dis.read(buffer) != -1); // keep reading until end of stream
			} finally {
				dis.close();
			}
		} catch (IOException e) {
			throw new SHA1HashException("Couldn't generate SHA1 hash for input stream.", e);
		}
		
		byte[] digest = dis.getMessageDigest().digest();
		return convertToHex(digest);
	}
	
}
